package data

import (
	"bytes"
	"crypto/sha1"
	"database/sql"
	"fmt"
	"strconv"
	"strings"
	"time"

	"gitchina/kly_service/errors"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

const (
	passwd_salt            = `@*%^^@IL;ldo123`
	Mbr_no_role_type       = "0"
	Mbr_cargo_owner_type   = "1"
	Mbr_carrier_owner_type = "2"
	Mbr_logistics_type     = "3"
	Mbr_ship_owner_type    = "4"
)

type MemberDto struct {
	Id         int    `orm:"pk; auto; column(id)`
	Name       string `orm:"column(name)"`
	NickName   string `orm:"column(nick_name)"`
	Passwd     string `orm:"column(password)"`
	Phone      string `orm:"column(phone)"`
	TypeId     int    `orm:"column(type_id)"`
	State      int    `orm:"column(state)"`
	Level      int    `orm:"column(level)"`
	AuthLevel  int    `orm:"column(auth_level)"`
	AuthState  int    `orm:"column(auth_state)"`
	Inviter    int    `orm:"column(inviter_id)"`
	PhotoUrl   string `orm:"column(photo)"`
	QRCode     string `orm:"column(qrcode)"`
	IDCardFS   string `orm:"column(id_card_fs)"`
	IDCardRS   string `orm:"column(id_card_rs)"`
	IDCardNo   string `orm:"column(id_card_no)"`
	Point      int    `orm:"column(point)"`
	ScoreTotal int    `orm:"column(score_total)"`
	ScoreCount int    `orm:"column(score_count)"`
}

type LogisticsEmpDto struct {
	Id     int    `orm:"pk;column(id)"`
	CorpId int    `orm:"column(corp_id)"`
	Intro  string `orm:"column(intro)"`
}

type LoginLogDto struct {
	Id         int    `orm:"pk;column(id)"`
	MemberId   int    `orm:"column(member_id)"`
	RegisterId string `orm:"column(register_id)"`
	LogType    int    `orm:"column(log_type)"`
	Platform   string `orm:"column(platform)"`
}

type PointDto struct {
	ID       int64  `orm:"pk; auto; column(id)`
	MemberID int    `orm:"column(member_id)"`
	Point    int    `orm:"column(point)"`
	Type     int    `orm:"column(type)"`
	Extra    string `orm:"column(extra)"`
}

type MemberRepo struct{}

func (m *MemberDto) TableName() string {
	return "m_member"
}

func (p *PointDto) TableName() string {
	return "t_point_log"
}

func (e *LogisticsEmpDto) TableName() string {
	return "m_logistics_emp"
}

func (l *LoginLogDto) TableName() string {
	return "m_login_log"
}

func (r MemberRepo) Exists(phone string) (exists bool, err error) {
	var num int64
	var list orm.ParamsList
	o := orm.NewOrm()
	num, err = o.Raw(SQL_Mbr_Exists, phone).ValuesFlat(&list)
	exists = num >= 1
	return
}

func (r MemberRepo) GetMemberIdByPhone(phone string) (id int, err error) {
	var num int64
	var list orm.ParamsList
	o := orm.NewOrm()
	if num, err = o.Raw(SQL_Mbr_Get_By_Phone, phone).ValuesFlat(&list); num >= 1 && err == nil {
		id, _ = strconv.Atoi(list[0].(string))
	} else {
		err = errors.New("carrier owner not found")
	}
	return
}

func (r MemberRepo) GetMemberIDByVehicleNO(no string) (id int, err error) {
	var num int64
	var list orm.ParamsList
	o := orm.NewOrm()
	if num, err = o.Raw(SQL_Mbr_Get_By_Vehicle_NO, no).ValuesFlat(&list); num >= 1 && err == nil {
		id, _ = strconv.Atoi(list[0].(string))
	} else {
		err = errors.New("carrier owner not found")
	}
	return
}

func (r MemberRepo) AddMember(dto *MemberDto) (id int64, err error) {
	o := orm.NewOrm()
	if err = o.Begin(); err == nil {
		dto.Passwd = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", dto.Passwd, passwd_salt))))
		if id, err = o.Insert(dto); err == nil {
			repo := PayRepo{o: o}
			if _, err = repo.AddWallet(WalletDto{MemberId: int(id)}); err == nil {
				o.Commit()
			} else {
				o.Rollback()
			}
		} else {
			o.Rollback()
		}
	} else {
		o.Rollback()
	}

	return
}

func (r MemberRepo) UpdateMemberClientInfo(memberId int, reigsterId, platform string) error {
	o := orm.NewOrm()
	_, err := o.Raw(SQL_Mbr_Upd_Client_Info, reigsterId, platform, memberId).Exec()
	return err
}

func (r MemberRepo) VerifyCode(phone, code string) (ok bool, err error) {
	var num int64
	var list orm.ParamsList
	o := orm.NewOrm()
	nowStr := time.Now().Format("2006-01-02 15:04:05")
	before5mStr := time.Now().Add(-time.Duration(5) * time.Minute).Format("2006-01-02 15:04:05")
	num, err = o.Raw(SQL_Mbr_Check_VerifyCode_Valid, phone, code, before5mStr, nowStr).ValuesFlat(&list)
	ok = err == nil && num >= 1
	if ok {
		o.Raw(SQL_Mbr_Invalid_VerifyCode, phone, code).Exec()
	}
	return
}

func (r MemberRepo) GetVerifyCodeSendPhoneAndIpCount(phone, clientIp string) (int, int, error) {
	var num int64
	var err error
	var phoneCount, ipCount int
	var list orm.ParamsList
	o := orm.NewOrm()
	now := time.Now()
	dateBegin := fmt.Sprintf("%s 00:00:00", now.Format("2006-01-02"))
	dateEnd := fmt.Sprintf("%s 23:59:59", now.Format("2006-01-02"))
	num, err = o.Raw(SQL_Mbr_Check_VerifyCode_Send_Phone_And_IP_Count, phone, dateBegin, dateEnd, clientIp, dateBegin, dateEnd).ValuesFlat(&list)
	if num == 1 {
		phoneCount, _ = strconv.Atoi(list[0].(string))
		ipCount, _ = strconv.Atoi(list[1].(string))
	}
	return phoneCount, ipCount, err
}

func (r MemberRepo) AddCode(phone, code, clientIp string) (ok bool, err error) {
	var num int64
	var result sql.Result
	var list orm.ParamsList
	o := orm.NewOrm()
	nowStr := time.Now().Format("2006-01-02 15:04:05")
	before1mStr := time.Now().Add(-time.Duration(1) * time.Minute).Format("2006-01-02 15:04:05")
	num, err = o.Raw(SQL_Mbr_Check_VerifyCode_Not_Sent, phone, before1mStr, nowStr).ValuesFlat(&list)
	if num == 0 && err == nil {
		result, err = o.Raw(SQL_Mbr_Insert_VerifyCode, phone, code, 1, time.Now().Hour(), clientIp).Exec()
		if err == nil {
			num, err = result.RowsAffected()
			ok = err == nil && num >= 1
		}
	}

	return
}

func (r MemberRepo) CheckPhoneAndPasswd(phone, passwd string) (mbr MemberDto, err error) {
	o := orm.NewOrm()
	pwd := fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", passwd, passwd_salt))))
	err = o.Raw(SQL_Mbr_Check_Phone_And_Passwd, phone, pwd).QueryRow(&mbr)
	return
}

func (r MemberRepo) AddQRCode(id int, qrcodeUrl string) (ok bool, err error) {
	var num int64
	var result sql.Result
	o := orm.NewOrm()
	result, err = o.Raw(SQL_Mbr_Upd_QRCode, qrcodeUrl, id).Exec()
	if err == nil {
		num, err = result.RowsAffected()
		ok = err == nil && num >= 1
	}
	return
}

func (r MemberRepo) GetMemberInfo(id int) (m MemberDto, err error) {
	m = MemberDto{Id: id}
	o := orm.NewOrm()
	err = o.Read(&m)
	return
}

func (r MemberRepo) GetMemberBriefInfo(ids []int) (list []MemberDto, err error) {
	o := orm.NewOrm()
	var buffer bytes.Buffer
	for _, id := range ids {
		buffer.WriteString(fmt.Sprintf("%d,", id))
	}
	_, err = o.Raw(fmt.Sprintf(SQL_Mbr_Get_Brief_Info, strings.TrimRight(buffer.String(), ","))).QueryRows(&list)
	return
}

func (r MemberRepo) UpdPasswdByPhone(phone, passwd string) (ok bool, err error) {
	var num int64
	var result sql.Result
	o := orm.NewOrm()
	passwd = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", passwd, passwd_salt))))
	result, err = o.Raw(SQL_Mbr_Upd_Passwd_By_Phone, passwd, phone).Exec()
	if err == nil {
		num, err = result.RowsAffected()
		ok = err == nil && num >= 1
	}
	return
}

func (r MemberRepo) UpdPasswordByPasswd(id int64, oldPasswd, newPasswd string) (ok bool, err error) {
	var num int64
	var result sql.Result
	o := orm.NewOrm()
	oldPasswd = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", oldPasswd, passwd_salt))))
	newPasswd = fmt.Sprintf("%x", sha1.Sum([]byte(fmt.Sprintf("%s%s", newPasswd, passwd_salt))))

	if result, err = o.Raw(SQL_Mbr_Upd_Passwd_By_Passwd, newPasswd, id, oldPasswd).Exec(); err == nil {
		num, err = result.RowsAffected()
		ok = err == nil && num >= 1
	}
	return
}

func (r MemberRepo) UpdateEmp(dto *LogisticsEmpDto) (err error) {
	var mbrType string
	mbr := &MemberDto{Id: dto.Id, TypeId: 3}
	o := orm.NewOrm()
	if _, err = o.Update(dto); err == nil {
		if mbrType, err = r.GetMemberType(dto.Id); err == nil {
			if mbrType == "0" {
				_, err = o.Update(mbr, "TypeId")
				return err
			}
		}
	}

	return
}

func (r MemberRepo) AddLoginLog(memberId, logType int, registerId, platform string) error {
	o := orm.NewOrm()
	_, err := o.Insert(&LoginLogDto{MemberId: memberId, LogType: logType, RegisterId: registerId, Platform: platform})
	return err
}

func (r MemberRepo) GetLoginRegisterId(memberId int) (registerId, platform string) {
	var list orm.ParamsList
	o := orm.NewOrm()
	if num, _ := o.Raw(SQL_Mbr_Get_RegisterId, memberId).ValuesFlat(&list); num >= 1 {
		registerId = list[0].(string)
		platform = list[1].(string)
	}
	return
}

func (r MemberRepo) UpdateNickName(memberID int, nickName string) error {
	o := orm.NewOrm()
	_, err := o.QueryTable("m_member").
		Filter("Id", memberID).
		Update(orm.Params{"nick_name": nickName})
	return err
}

func (r MemberRepo) UpdatePhoneAndState(memberID int, phone string, state int) error {
	o := orm.NewOrm()
	_, err := o.QueryTable("m_member").Filter("Id", memberID).Update(orm.Params{"phone": phone, "State": state})
	return err
}

func (r MemberRepo) AddPoint(point PointDto) (err error) {
	var count int64
	var list []PointDto
	o := orm.NewOrm()
	if count, err = o.Raw(SQL_Mbr_Get_Last_Point_Log, point.MemberID, point.Type).QueryRows(&list); err == nil {
		if count == 0 {
			err = doAddPoint(point)
		} else {
			if point.Extra != list[0].Extra {
				err = doAddPoint(point)
			}
		}
	}
	return err
}

func doAddPoint(point PointDto) (err error) {
	o := orm.NewOrm()
	if err = o.Begin(); err == nil {
		if _, err = o.Insert(&point); err == nil {
			_, err = o.Raw(SQL_Mbr_Update_Point, point.Point, point.MemberID).Exec()
		}

		if err == nil {
			o.Commit()
		} else {
			o.Rollback()
		}
	}
	return
}

func init() {
	orm.RegisterModel(new(MemberDto), new(LogisticsEmpDto), new(LoginLogDto), new(PointDto))
}
